/*
 * LevelUploadingGameStateController.cpp
 *
 *  Created on: Apr 6, 2012
 *      Author: erudhes
 */
#include <wx/filename.h>
#include <wx/socket.h>
#include "LevelUploadingGameStateController.h"
#include "NetworkEventData.h"
#include "Configuration.h"
#include "MainFrame.h"
#include "MainPanel.h"
#include "IGameController.h"
#include "App.h"

LevelUploadingGameStateController::LevelUploadingGameStateController(const wxString& fileName, SmartPointer<ServerStartingGameStateController> serverStartingController): mStaticDrawController(NULL, false),
																																										  mUploadedFile(),
																																										  mUploadedFileLength(0),
																																										  mUploadedBytes(0),
																																										  mProgressDialog(NULL),
																																										  mGauge(NULL),
																																										  mDirectoryPath(),
																																										  mFileName(fileName),
																																										  mIsUploadedFinished(false),
																																										  mServerStartingController(serverStartingController)
{
	wxChar pathSeparator = wxFileName::GetPathSeparator();
	const wxString& level = wxGetApp().getActualLevel();
	wxString filePath;
	SmartPointer<NetworkEventData> data = NULL;
	wxSocketBase* clientSocket = wxGetApp().getClientSocket();
	wxString progressDialogTitle = wxT("Downloading ");
	int fileSize = 0;

	// Load the level to the background
	mDirectoryPath = wxGetCwd() + pathSeparator + wxGetApp().getConfiguration()->getLevels()->getPath() + pathSeparator + level + pathSeparator;
	mStaticDrawController.setBitmap(new wxBitmap(mDirectoryPath + level + wxT(".bmp"), wxBITMAP_TYPE_BMP));
	mStaticDrawController.refreshImage(false);
	wxGetApp().getMainFrame()->GetStatusBar()->SetStatusText(wxT("Uploading the level to the client"));
	progressDialogTitle += mFileName;
	// Create the progress dialog
	mProgressDialog = new wxDialog(NULL, wxID_ANY, progressDialogTitle);
	mProgressDialog->SetClientSize(wxSize(400, 20));
	mGauge = new wxGauge(mProgressDialog, wxID_ANY, 100, wxPoint(0, 0), wxSize(400, 20));
	mGauge->SetValue(0);
	mProgressDialog->Show(true);
	// Read the file we need to upload first
	filePath = mDirectoryPath + mFileName;
	mUploadedFile.Open(filePath.c_str(), wxFile::read);
	mUploadedFileLength = mUploadedFile.Length();
	mUploadedBytes = 0;
	// Send the acknowledge with the size of the file to the client
	data = new NetworkEventData();
	data->setDataType(NetworkEventData::DOWNLOADING_START_ACK);
	fileSize = htonl(mUploadedFileLength);
	data->setRawData((char*)(&fileSize), sizeof(int));
	if(false == data->write(clientSocket))
	{
		// Drop the connection to be able to handle an other one
		clientSocket->Destroy();
		clientSocket = NULL;
		wxGetApp().setClientSocket(NULL);
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not start uploading"), MainFrame::SERVER_ERROR);
		// Continue listening for clients
		wxGetApp().getMainFrame()->getMainPanel()->getGameController()->changeState(new ServerStartingGameStateController(wxGetApp().getActualLevel()));

		return;
	}
	// Send the first data segment of the file
	this->uploadFile();
	wxGetApp().getMainFrame()->getMainPanel()->Refresh();
}

/*virtual*/
LevelUploadingGameStateController::~LevelUploadingGameStateController()
{
	if(true == mUploadedFile.IsOpened())
		mUploadedFile.Close();
	if(NULL != mProgressDialog)
	{
		mProgressDialog->Destroy();
		mProgressDialog = NULL;
	}
}

//
// Methods inherited from Controller
//

void LevelUploadingGameStateController::handleMouseDown(const wxPoint&)
{
	// Nothing to do
}

void LevelUploadingGameStateController::handleMouseUp(const wxPoint&)
{
	// Nothing to do
}

bool LevelUploadingGameStateController::handleMouseMotion(const wxPoint&)
{
	// Nothing to do
	return false;
}

const wxBitmap& LevelUploadingGameStateController::getBitmapToPaint() const
{
	return mStaticDrawController.getBitmapToPaint();
}

wxBitmap& LevelUploadingGameStateController::getBitmapToDraw()
{
	return mStaticDrawController.getBitmapToDraw();
}

//
// Methods inherited from GameStateController
//

void LevelUploadingGameStateController::handleSocketConnection(wxSocketEvent&)
{
	wxSocketServer* serverSocket = static_cast<wxSocketServer*>(wxGetApp().getServerSocket());
	SmartPointer<NetworkEventData> data = new NetworkEventData();

	wxASSERT_MSG(serverSocket != NULL, wxT("LevelUploadingGameStateController::handleSocketConnection called on client side. Could not happen!"));
	data->rejectConnection(serverSocket);
}

void LevelUploadingGameStateController::handleSocketLost(wxSocketEvent& socketEvent)
{
	wxSocketBase* socket = wxGetApp().getClientSocket();

	if(socketEvent.GetSocket() == socket)
	{
		// The served client has been disconnected
		socket->Destroy();
		socket = NULL;
		wxGetApp().setClientSocket(NULL);
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Connection lost!"), MainFrame::SERVER_ERROR);
		wxGetApp().getMainFrame()->getMainPanel()->getGameController()->changeState(new ServerStartingGameStateController(wxGetApp().getActualLevel()));
	}
}

void LevelUploadingGameStateController::handleSocketInput(wxSocketEvent& socketEvent)
{
	SmartPointer<NetworkEventData> data = NULL;
	SmartPointer<Configuration::CommonConfigurationPacket> configurationData = NULL;
	wxSocketBase* clientSocket = wxGetApp().getClientSocket();
	IGameController* gameController = wxGetApp().getMainFrame()->getMainPanel()->getGameController();
	wxString filePath;
	int fileSize = 0;

	// If the event happened on an other socket, there is nothing to do
	if(socketEvent.GetSocket() != clientSocket)
		return;
	wxASSERT_MSG(NULL != clientSocket, wxT("Client socket is NULL, when receiving data. Could not be!"));
	data = new NetworkEventData();
	// Check if an error occurred or not
	if(false == data->read(clientSocket))
	{
		clientSocket->Destroy();
		clientSocket = NULL;
		wxGetApp().setClientSocket(NULL);
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Failed to receive data from the client"), MainFrame::SERVER_ERROR);
		// Revert back to ServerStarting state
		gameController->changeState(new ServerStartingGameStateController(wxGetApp().getActualLevel()));

		return;
	}
	switch(data->getDataType())
	{
		case NetworkEventData::DOWNLOADING_START:
			mFileName = data->getStringData();
			filePath = mDirectoryPath + mFileName;
			// Close the earlier file, and open the new one
			if(true  == mUploadedFile.IsOpened())
				mUploadedFile.Close();
			mUploadedFile.Open(filePath.c_str(), wxFile::read);
			mUploadedFileLength = mUploadedFile.Length();
			mUploadedBytes = 0;
			mProgressDialog->SetTitle(wxT("Downloading ") + mFileName);
			mGauge->SetValue(0);
			// Send the acknowledge with the size of the file to the client
			data = new NetworkEventData();
			data->setDataType(NetworkEventData::DOWNLOADING_START_ACK);
			fileSize = htonl(mUploadedFileLength);
			data->setRawData((char*)(&fileSize), sizeof(int));
			if(false == data->write(clientSocket))
			{
				// Drop the connection to be able to handle an other one
				clientSocket->Destroy();
				clientSocket = NULL;
				wxGetApp().setClientSocket(NULL);
				wxGetApp().getMainFrame()->showMessageDialog(wxT("Error during uploading level"), MainFrame::SERVER_ERROR);
				// Continue listening for clients
				wxGetApp().getMainFrame()->getMainPanel()->getGameController()->changeState(new ServerStartingGameStateController(wxGetApp().getActualLevel()));

				return;
			}
			mIsUploadedFinished = false;
			this->uploadFile();
			break;
		case NetworkEventData::DOWNLOADING_DATA_ACK:
			// The client acknowledged the data we sent before
			if(true == mIsUploadedFinished)
			{
				// If the upload is ready we can send the DOWNLOADING_END packet, the client will send back the CONNECTING_ACK or ask for an other download
				mUploadedFile.Close();
				data = new NetworkEventData();
				data->setDataType(NetworkEventData::DOWNLOADING_END);
				if(false == data->write(clientSocket))
				{
					// Drop the connection to be able to handle an other one
					clientSocket->Destroy();
					clientSocket = NULL;
					wxGetApp().setClientSocket(NULL);
					// Revert back to ServerStarting game state to be able to serve an other client
					gameController->changeState(new ServerStartingGameStateController(wxGetApp().getActualLevel()));
					wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not finish uploading!"), MainFrame::SERVER_ERROR);

					return;
				}
			}
			else
			{
				// We have more data to upload
				this->uploadFile();
			}
			break;
		case NetworkEventData::CONNECTING_ACK:
			// Close the progress dialog
			mGauge->SetValue(100);
			mProgressDialog->Close(true);
			mProgressDialog->Destroy();
			mProgressDialog = NULL;
			mGauge = NULL;
			mServerStartingController->handleConfigurationNegotioation();
			// Switch back to ServerStarting state to handle the configuration negotiation
			gameController->changeState(mServerStartingController);
			break;
		default:
			wxASSERT_MSG(false, wxT("Other than DOWNLOADING_DATA_ACK or CONNECTING_ACK message type arrived! Should not happened!"));
			break;
	}
}

void LevelUploadingGameStateController::handleSocketOutput(wxSocketEvent&)
{
	wxASSERT_MSG(false, wxT("LevelUploadingGameStateController::handleSocketOutput called. It could not happen!"));
}

/**
 * Private methods
 */

bool LevelUploadingGameStateController::uploadFile()
{
	static int counter = 0;
	static int depth = 0;
	bool returnValue = false;

	counter++;
	depth++;
	if(false == mUploadedFile.IsOpened())
	{
		wxString filePath = mDirectoryPath + mFileName;

		mUploadedFile.Open(filePath.c_str(), wxFile::read);
	}
	switch(this->uploadData())
	{
		case NO_ERROR_DATA_REMAINED:
			// Nothing to do. Client will send an acknowledge and we can send the remaining part there
			returnValue = true;
			break;
		case NO_ERROR_FINISHED:
			// Nothing to do. Client will send the acknowledge
			mIsUploadedFinished = true;
			returnValue = true;
			break;
		case FILE_IO_ERROR:
			// If we are just wanted to start the uploading, we can try to do it again.
			// TODO: Open the file, before retrying upload it
			mUploadedFile.Close();
			if(1 == counter && 1 == depth)
				returnValue = this->uploadFile();
			else
			{
				// TODO: Send an error packet before dropping the connection
				// This is a permanent error, we can not upload the file
				// Drop the connection to be able to handle an other one
				wxGetApp().getClientSocket()->Destroy();
				wxGetApp().setClientSocket(NULL);
				// Revert back to ServerStarting game state to be able to serve an other client
				wxGetApp().getMainFrame()->getMainPanel()->getGameController()->changeState(new ServerStartingGameStateController(wxGetApp().getActualLevel()));
				wxGetApp().getMainFrame()->showMessageDialog(wxT("Could not upload the file to the client!"), MainFrame::SERVER_ERROR);
				// returnValue remains false
			}
			break;
		case NETWORK_IO_ERROR:
			// Nothing to do application is already reverted back to ServerStarting state - returnValue remains false
			break;
		default:
			wxASSERT_MSG(false, wxT("Unknown output from LevelUploadingGameStateController::uploadData!"));
			break;
	}
	depth--;

	return returnValue;
}

LevelUploadingGameStateController::UploadError LevelUploadingGameStateController::uploadData()
{
	const int bufferSize = 1024;
	char buffer[bufferSize] = {0};
	int readBytes = 0;
	wxSocketBase* clientSocket = NULL;
	SmartPointer<NetworkEventData> data = NULL;
	IGameController* gameController = wxGetApp().getMainFrame()->getMainPanel()->getGameController();

	// Try to read from the file
	if(false == mUploadedFile.IsOpened())
		return FILE_IO_ERROR;
	readBytes = mUploadedFile.Read(buffer, bufferSize);
	if(readBytes == wxInvalidOffset)
		return FILE_IO_ERROR;
	// Upload the read bytes
	clientSocket = wxGetApp().getClientSocket();
	wxASSERT_MSG(NULL != clientSocket, wxT("Client socket is NULL while uploading level. It could not happen!"));
	data = new NetworkEventData();
	data->setDataType(NetworkEventData::DOWNLOADING_DATA);
	data->setRawData(buffer, readBytes);
	if(false == data->write(clientSocket))
	{
		// Drop the connection to be able to handle an other one
		clientSocket->Destroy();
		clientSocket = NULL;
		wxGetApp().setClientSocket(NULL);
		// Revert back to ServerStarting game state to be able to serve an other client
		gameController->changeState(new ServerStartingGameStateController(wxGetApp().getActualLevel()));
		wxGetApp().getMainFrame()->showMessageDialog(wxT("Error during uploading level!"), MainFrame::SERVER_ERROR);

		return NETWORK_IO_ERROR;
	}
	mUploadedBytes += data->getDataLength();
	mGauge->SetValue((static_cast<float>(mUploadedBytes)/static_cast<float>(mUploadedFileLength)) * 100);
	// If we could not read the maximum number of bytes from the file, the end is reached
	if(readBytes != bufferSize)
		return NO_ERROR_FINISHED;

	return NO_ERROR_DATA_REMAINED;
}
